package vip.youngboy.longpoll.service;

import com.google.common.eventbus.Subscribe;
import org.springframework.stereotype.Service;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.*;

@Service
public class LongPollService {

    final Map<String,ClientLongPolling> allSubs;

    final ScheduledExecutorService scheduler;

    public LongPollService() {
        allSubs = new ConcurrentHashMap<>();
        scheduler = Executors.newScheduledThreadPool(1, r -> {
            Thread t = new Thread(r);
            t.setDaemon(true);
            t.setName("vip.youngoby.LongPolling");
            return t;
        });
        //监控任务
        scheduler.scheduleWithFixedDelay(new StatTask(), 0L, 10L, TimeUnit.SECONDS);
    }


    @Subscribe
    public void listenScan(ScanEvent event) {
        //用户扫码事件
        scheduler.execute(new DataChangeTask(event.getUuid()));
    }

    @Subscribe
    public void listenConfirm(ConfirmEvent confirmEvent) {
        //用户扫码事件
        scheduler.execute(new ConfirmTask(confirmEvent));
    }

    public void addLongPollingClient(HttpServletRequest req, HttpServletResponse response, String uuid, String type) {
        //获取异步上下文对象
        // 一定要由HTTP线程调用，否则离开后容器会立即发送响应
        final AsyncContext asyncContext = req.startAsync();
        // AsyncContext.setTimeout()的超时时间不准，所以只能自己控制
        asyncContext.setTimeout(0L);
        //异步执行添加操作
        if(type.equals("confirm")){
            scheduler.execute(new ClientLongPolling(asyncContext,uuid,"",10000,"confirm","confirm"));
        }else{
            scheduler.execute(new ClientLongPolling(asyncContext,uuid,"",10000,"scan","scan"));
        }
    }


    class StatTask implements Runnable {
        @Override
        public void run() {
            //状态检查任务
            System.out.println("线程池中的任务数："+allSubs.size());
        }
    }

    class ClientLongPolling implements Runnable {

        @Override
        public void run() {
            asyncTimeoutFuture = scheduler.schedule(new Runnable() {
                public void run() {
                    try {
                        /**
                         * 时间到了删除订阅关系
                         */
                        System.out.println("执行删除订阅关系操作"+uuid);
                        allSubs.remove(uuid);

                        if (isFixedPolling()) {

                            if (true) {
                                sendResponse("Event to auto return");
                            } else {
                                sendResponse(null);
                            }
                        } else {
                            sendResponse(null);
                        }
                    } catch (Throwable t) {
                        System.err.println(t.getLocalizedMessage());
                    }

                }

                private boolean isFixedPolling() {
                    return true;
                }

            }, timeoutTime, TimeUnit.MILLISECONDS);

            allSubs.put(uuid,this);
        }

        void sendResponse(String msg) {
            /**
             *  取消超时任务
             */
            if (null != asyncTimeoutFuture) {
                asyncTimeoutFuture.cancel(false);
            }
            generateResponse(msg);
        }

        void generateResponse(String msg) {
            if (null == msg) {
                /**
                 * 告诉容器发送HTTP响应
                 */
                asyncContext.complete();
                return;
            }

            //使用的是Servlet 中的 AsyncContext
            HttpServletResponse response = (HttpServletResponse)asyncContext.getResponse();

            try {
                // 禁用缓存
                response.setHeader("Pragma", "no-cache");
                response.setDateHeader("Expires", 0);
                response.setHeader("Cache-Control", "no-cache,no-store");
                response.setStatus(HttpServletResponse.SC_OK);
                response.getWriter().println(msg);
                asyncContext.complete();
            } catch (Exception se) {
                asyncContext.complete();
            }
        }

        ClientLongPolling(AsyncContext ac, String uuid, String ip,
                          long timeoutTime, String appName, String tag) {
            this.asyncContext = ac;
            this.uuid = uuid;
            this.timeoutTime = timeoutTime;
        }

        // =================

        final AsyncContext asyncContext;
        final String uuid;
        final long timeoutTime;

        Future<?> asyncTimeoutFuture;
    }


    class DataChangeTask implements Runnable {
        @Override
        public void run() {
            try {
                //拿到对应的 clientSub 返回数据
                ClientLongPolling clientLongPolling = allSubs.get(result);
                if(clientLongPolling!=null){
                    clientLongPolling.sendResponse("scanned uuid : "+result+" Please click to confirm authorization");
                }
                //删除订阅关系
                allSubs.remove(result);
            } catch (Throwable t) {
                System.err.println(t.getLocalizedMessage());
            }
        }


        DataChangeTask(String result) {
            this.result=result;
        }

        final String result;
    }

    class ConfirmTask implements Runnable {
        @Override
        public void run() {
            try {
                //拿到对应的 clientSub 返回数据
                ClientLongPolling clientLongPolling = allSubs.get(confirmEvent.getUuid());
                String result = confirmEvent.getResult();

                if(clientLongPolling!=null){
                    if(result.equals("success")){
                        //扫码成功并确认，获取上下文用户信息返回token
                        clientLongPolling.sendResponse("auth success token is: {your token generation strategy}");
                    }else{
                        clientLongPolling.sendResponse("auth failed");
                    }
                }
                //删除订阅关系
                allSubs.remove(confirmEvent.getUuid());
            } catch (Throwable t) {
                System.err.println(t.getLocalizedMessage());
            }
        }


        ConfirmTask(ConfirmEvent confirmEvent) {
            this.confirmEvent=confirmEvent;
        }

        final ConfirmEvent confirmEvent;
    }

}
